/*
 * JSF Toolkit Component Framework
 * Copyright (C) 2007 Noah Sloan <iamnoah A-T gmail D0T com>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 */
package com.jsftoolkit.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.util.Iterator;

public class UpdateSourceHeaders {

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		UpdateSourceHeaders updater = new UpdateSourceHeaders("/HEADER.txt");

		File[] srcDirs = { new File("src/main/java"), new File("src/test/java") };
		for (File dir : srcDirs) {
			// find .java files
			updater.processDir(dir);
		}
	}

	private final Iterator<InputStream> header;

	public UpdateSourceHeaders(final String resource) {
		this(new Utils.Factory<InputStream>() {
			public InputStream next() {
				return getClass().getResourceAsStream(resource);
			}
		});
	}

	public UpdateSourceHeaders(final File header) {
		this(new Utils.Factory<InputStream>() {
			public InputStream next() {
				try {
					return new FileInputStream(header);
				} catch (FileNotFoundException e) {
					throw new RuntimeException(e);
				}
			}
		});
	}

	public UpdateSourceHeaders(Iterator<InputStream> header) {
		super();
		this.header = header;
	}

	public void processDir(File dir) throws IOException {
		File[] files = dir.listFiles(new FileFilter() {
			public boolean accept(File pathname) {
				return pathname.isDirectory()
						|| pathname.getName().endsWith(".java");
			}
		});
		for (File file : files) {
			if (file.isDirectory()) {
				processDir(file);
			} else {
				assert file.getName().endsWith(".java");
				updateHeader(file);
			}
		}
	}

	protected void updateHeader(File file) throws IOException {
		// create a temp file to write the changes to
		File temp = File.createTempFile("USH", "updated");
		FileOutputStream tOut = new FileOutputStream(temp);

		// write the new header to the temp file
		Utils.writeAll(header.next(), tOut);

		// create a reader for the existing file
		Reader reader;
		try {
			reader = new FileReader(file);
		} catch (FileNotFoundException e) {
			assert file.exists();
			// should never happen
			throw new RuntimeException(e);
		}

		int c = eatWhitespace(reader);

		// create a writer to the temp file
		OutputStreamWriter writer = new OutputStreamWriter(tOut);

		// check for an existing header comment
		if (c == '/') {
			c = reader.read();
			if (c == '*') {
				// there is already a header comment, so skip past it
				Utils.writeAll(new DelimitedReader(reader, "*/"),
						new NullWriter());
				c = -1;
			} else {
				// starts with something other than a header comment, so restore
				// the '/'
				writer.write('/');
			}
		}

		// if we eat the header comment, eat any trailing whitespace
		c = c == -1 ? eatWhitespace(reader) : c;
		if (c != -1) {
			// we've read an extra (non comment or whitespace) character at this
			// point, so write it back
			writer.write(c);
		}

		// copy the rest of the original file
		Utils.writeAll(reader, writer);
		writer.close();

		// copy the temp file over the original
		Utils.writeAll(new FileInputStream(temp), new FileOutputStream(file));
	}

	protected int eatWhitespace(Reader reader) throws IOException {
		int c;
		// eat whitespace at the start
		while (Character.isWhitespace((c = reader.read())))
			;
		return c;
	}

}
